package com.ark.frigate.tools.common.exception;

import com.ark.frigate.tools.response.ResponseData;
import com.ark.frigate.tools.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * 该类抽象封装了HTTP常用的异常处理，使用时继承该类,并注上 @CollectionAdvice 使用
 * 实现sendOnOffMessage 方法，可接收异常及 message
 * @author zengweilong
 */
@Slf4j
public abstract class GlobalAbstractExceptionHandle {

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("请求方法参数校验不通过url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);

        String paramMessage = e.getBindingResult().getAllErrors()
                .stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                .findFirst().orElse("参数校验不通过");
        return ResponseData.buildErrorResp(paramMessage);
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handlerConstraintViolationException(ConstraintViolationException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("请求方法参数校验不通过url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);

        String paramMessage = e.getConstraintViolations()
                .stream().map(ConstraintViolation::getMessage)
                .findFirst().orElse("参数校验不通过");
        return ResponseData.buildErrorResp(paramMessage);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handlerHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("参数校验不通过url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);
        String paramMessage = e.getCause().getLocalizedMessage();
        if (StringUtils.isNotBlank(paramMessage)) {
            paramMessage = paramMessage.substring(0, paramMessage.indexOf("\n"));
        }
        return ResponseData.buildErrorResp(paramMessage);
    }

    /**
     * 处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleBindException(BindException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("请求参数值绑定错误url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);
        String paramMessage = e.getBindingResult().getAllErrors()
                .stream().map(DefaultMessageSourceResolvable::getDefaultMessage).findFirst().orElse("参数校验不通过");
        return ResponseData.buildErrorResp(paramMessage);
    }


    /**
     * @param e
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("参数错误 url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);
        String paramMessage = e.getMessage();
        return ResponseData.buildErrorResp(paramMessage);
    }

    /**
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseData handleIllegalArgumentException(IllegalArgumentException e) {
        String url = getRequestUri();
        String loggerMessage = String.format("数据类型转化错误 url=%s, exMsg=%s", url, e.getMessage());
        sendOnOffMessage(loggerMessage, e);
        String paramMessage = e.getLocalizedMessage();
        return ResponseData.buildErrorResp(paramMessage);
    }

    /**
     * 获取请求上下文
     *
     * @return
     */
    protected String getRequestUri() {
        HttpServletRequest request = RequestUtils.getRequest();
        if (request == null) {
            log.warn("获取request失败");
            return StringUtils.EMPTY;
        }
        return request.getRequestURI();
    }

    /**
     * 异常消息发送
     * @param loggerMessage
     * @param ex
     */
    public abstract void sendOnOffMessage(String loggerMessage, Exception ex);
}
